﻿#include "facade.h"
#include "ui_facade.h"

Facade::Facade(QWidget *parent) :
    BaseFacade(parent),
    ui(new Ui::Facade)
{
    ui->setupUi(this);

    form = Form_None;
    isWorking=false;
    note=nullptr;
    note2=nullptr;
    popup=nullptr;
    dbTry_failure=0;
    searchBox=nullptr;
    autoHideInterval=AUTOHIDE_INTERVAL;
    monitorInterval=MONITOR_INTERVAL;
    receiver=nullptr;

    QDir cacheDir(cacheLocation);
    if(!cacheDir.exists())
        cacheDir.mkpath(cacheLocation);

    QDir dataDir(dataLocation);
    if(!dataDir.exists())
        dataDir.mkpath(dataLocation);

    startUp();
}

Facade::~Facade()
{
    servantThread.quit();
    servantThread.wait();

    if(config.isReceiver)
        endReceiver();

    if(db.isOpen())
        db.close();

    delete ui;
}

void Facade::initializeServant()
{
    qRegisterMetaType<QCryptographicHash::Algorithm>("QCryptographicHash::Algorithm");

    worker=new Servant;

    connect(&servantThread,&QThread::finished,worker,&QObject::deleteLater);
    connect(this,SIGNAL(sendRequest(QCryptographicHash::Algorithm,QString)),
            worker,SLOT(verifyHash(QCryptographicHash::Algorithm,QString)));
    connect(worker,SIGNAL(sendResult(QCryptographicHash::Algorithm,QByteArray)),
            this,SLOT(receiveResult(QCryptographicHash::Algorithm,QByteArray)));

    worker->moveToThread(&servantThread);
    servantThread.start();
}

bool Facade::connectStorage()
{
    if(!QFile::exists(LOC_CONFIG))
        return false;

    db=sp_createStorage(LOC_CONFIG);
    return db.open();
}

void Facade::startUp()
{
    if(config.isAdvanced)
        setAcceptDrops(true);

    if(config.isMonitor)
    {
        QString date=QDate::currentDate().toString("yyyyMMdd");
        shotLocation=genericDataLocation + PRE_SCRSHOTS + date;
        QDir dir(shotLocation);
        if(!dir.exists())
            dir.mkpath(shotLocation);

        monitorId=startTimer(monitorInterval);
    }

    setSkin();

    defPos.setX(d_width - DEF_WID);
    defPos.setY(200);
    moveDefPos();
    if(config.isReceiver)
        startReceiver();

    initializeServant();

    if(!connectStorage())
        say(QStringLiteral("异常情况，无法连接到数据库！"));
    else
        cmdStore.connectStorage(db,TD_REF_CMD);

    createMenu();

    autoHideId=startTimer(autoHideInterval);
    connect(this,&TransFacade::trayStateChanged,this,&Facade::onTrayStateChanged);

    createTray(QIcon(":/app.ico"));
    if(config.isAdvanced)
        tray->setToolTip(QString::number(SP_VER) + QStringLiteral(" - SinglePoint 运行中【高级模式】"));
    else
        tray->setToolTip(QString::number(SP_VER) + QStringLiteral(" - SinglePoint 运行中"));
}

void Facade::onTrayStateChanged()
{
    killTimer(autoHideId);
    autoHideId=startTimer(autoHideInterval);
}

void Facade::startReceiver()
{
    receiver=new BaseReceiver;
    connect(receiver,&BaseReceiver::received,this,&Facade::receivedMessage);
    connect(&recvThread,&QThread::finished,receiver,&QObject::deleteLater);
    receiver->moveToThread(&recvThread);
    recvThread.start();
}

void Facade::endReceiver()
{
    recvThread.quit();
    recvThread.wait();
}

void Facade::receivedMessage(const QByteArray &data)
{
    parseCommand(QString(data));
}

void Facade::parseCommand(const QString &cmd)
{
    QString request=cmd;
    if(request.isEmpty())
        return;

    if(request.startsWith("#"))
    {
        request=request.remove("#");
        int code=cmdStore.parseCommand(request);
        if(code != CMD_NULL)
            executeCommand(code);
        else
            say(QStringLiteral("不识别的命令"));
    }
}

void Facade::executeCommand(int code)
{
    switch (code) {
    case CMD_RELOAD_MENU:
        break;
    case CMD_SCREEN_SHOT:
        cmd_screenShot();
        break;
    }
    say(QStringLiteral("命令已执行"));
}

void Facade::cmd_screenShot()
{
    QPixmap pix=qApp->primaryScreen()->grabWindow(0);
    if(pix.isNull())
        return;

    if(shotLocation.isEmpty())
    {
        QString date=QDate::currentDate().toString("yyyyMMdd");
        shotLocation=genericDataLocation + PRE_SCRSHOTS + date;
    }

    QDir dir(shotLocation);
    if(!dir.exists())
        dir.mkpath(shotLocation);

    QString time=QDateTime::currentDateTime().toString("hhmmss");
    QString fileName=time + ".png";
    QString filePath=shotLocation + "/" + fileName;

    QFile file(filePath);
    file.open(QFile::WriteOnly);
    pix.save(&file,"PNG");
}

void Facade::timerEvent(QTimerEvent *event)
{
    int id=event->timerId();

    if(id == autoHideId)
    {
        if(!isActiveWindow())
        {
            if(isVisible())
                this->hide();
        }
    }
    else if(id == monitorId)
    {
        if(config.isMonitor)
            cmd_screenShot();
    }
}

void Facade::aboutVersion()
{
    QString info=QStringLiteral("附加信息：\n");
    ::sp_aboutVersionAddition(info);
}

void Facade::setSkin()
{
    switch (getSkin()) {
    case sk_gre:
        skin_gre;
        break;
    case sk_blu:
        skin_blu;
        break;
    case sk_pik:
        skin_pik;
        break;
    default:
        skin_blu;
        break;
    }
}

void Facade::dragEnterEvent(QDragEnterEvent *event)
{
    if(form == Form_None || isWorking)
        return;

    //如果为文件，则支持拖放
    if(event->mimeData()->hasFormat("text/uri-list"))
        event->acceptProposedAction();
}

void Facade::dropEvent(QDropEvent *event)
{
    if(form == Form_None)
        return;

    QList<QUrl> urls = event->mimeData()->urls();
    if(urls.isEmpty())
        return;

    QString fileName=urls.first().toLocalFile();
    QFileInfo fileInfo(fileName);
    int multiple;
    if(fileInfo.size() >= BASE_SIZE)
        multiple=fileInfo.size() / BASE_SIZE;
    else
        multiple=4;

    if(form == Form_MD5)
    {
        say(QStringLiteral("计算中..."),multiple);
        isWorking=true;
        emit sendRequest(QCryptographicHash::Md5,fileName);
    }
    else if(form == Form_SHA1)
    {
        say(QStringLiteral("计算中..."),multiple);
        isWorking=true;
        emit sendRequest(QCryptographicHash::Sha1,fileName);
    }
}

void Facade::keyPressEvent(QKeyEvent *event)
{
    if(event->modifiers() == Qt::AltModifier && event->key()==Qt::Key_1)
        showNote();
    else if(event->modifiers() == Qt::AltModifier && event->key()==Qt::Key_2)
        showNote2();
    else if(event->modifiers() == Qt::AltModifier && event->key()==Qt::Key_F2)
        showSearchBox();
    // else if(event->modifiers() == Qt::AltModifier && event->key()==Qt::Key_W)
    //    showThroughPanel();
    //    else if(event->modifiers() == Qt::AltModifier && event->key()==Qt::Key_P)
    //    util_grubScreen();
    else
    {
        switch(event->key())
        {
        case Qt::Key_Home:
            moveDefPos();
            break;
        case Qt::Key_F1:
            QDesktopServices::openUrl(QUrl::fromLocalFile(README));
            break;
        case Qt::Key_F3:
            invokeCalc();
            break;
        case Qt::Key_J:
            util_verifyMD5();
            break;
        case Qt::Key_K:
            util_verifySHA1();
            break;
        case Qt::Key_L:
            util_stringLength();
            break;
        case Qt::Key_U:
            util_generateUuid();
            break;
        case Qt::Key_M:
            util_magnet();
            break;
        case Qt::Key_B:
            util_baiduPan();
            break;
        case Qt::Key_W:
            util_webThrough();
            break;
            //        case Qt::Key_P:
            //          util_grubRect();
            //          break;
        case Qt::Key_Backspace:
            util_reset();
            break;
        }
    }
}

void Facade::mouseDoubleClickEvent(QMouseEvent *)
{
    if(form == Form_None)
        return;

    QString word=qApp->clipboard()->text();
    switch (form) {
    case Form_Magnet:
        if(word.startsWith("magnet:"))
        {
            QDesktopServices::openUrl(QUrl(word));
            return;
        }
        word.remove(QRegExp("\\s"));
        word = MAG_URL + word;
        qApp->clipboard()->setText(word);
        QDesktopServices::openUrl(QUrl(word));
        break;
    case Form_Pan:
        if(word.startsWith("http://"))
        {
            QDesktopServices::openUrl(QUrl(word));
            return;
        }
        word.remove(QRegExp("\\s"));
        word = PAN_URL + word;
        qApp->clipboard()->setText(word);
        QDesktopServices::openUrl(QUrl(word));
        break;
    case Form_StrLen:
        QMessageBox::information(this,QStringLiteral("字符串长度"),QStringLiteral("当前字符串长度为： ") + QString::number(word.length()));
        break;
    default:
        if(word.startsWith("http"))
            QDesktopServices::openUrl(QUrl(word));
        break;
    }
}

bool Facade::db_try()
{
    if(!db.isOpen())
        if(!db.open())
        {
            dbTry_failure += 1;
            return false;
        }
        else
            return true;
    else
        return true;
}

void Facade::createMenu()
{
    menu=new QMenu(this);
    generateMenu();
}

void Facade::reloadMenu()
{
    menuItems.clear();
    menu->clear();
    menu->disconnect();
    generateMenu();
}

void Facade::generateMenu()
{
    if(!db_try())
    {
        menu->addAction(QStringLiteral("退出"),QApplication::quit);
        return;
    }

    createLocationMenu();
    createAppMenu();
    createPropsMenu();
    createToolMenu();
    createSysToolMenu();
    createCommandMenu();
    createOptionMenu();
    menu->addSeparator();
    createToolboxMenu();
    createFavoriteMenu();
    db.close();
}

void Facade::createToolboxMenu()
{
    if(!config.isUtility)
        return;

    QDir dir(FHS_OPT);
    if(!dir.exists())
        return;

    QStringList dirList=dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
    if(dirList.size() < 1)
        return;

    if(!toolboxMenuItems.isEmpty())
        toolboxMenuItems.clear();

    QMenu *m_toolbox=menu->addMenu(QStringLiteral("工具箱"));
    foreach (QString folder, dirList) {
        folder = P_FHS_OPT + folder;
        QString propFile=folder + "/prop.json";
        if(QFile::exists(propFile)){
            Pie pie(propFile);
            if(pie.load())
            {
                QString label=pie.get("label");
                QString entry=folder + QString("/")  + pie.get("entry");
                toolboxMenuItems.insert(label,entry);
                m_toolbox->addAction(label);
            }
        }
    }
    connect(m_toolbox,&QMenu::triggered,this,&Facade::processToolboxAction);
    menu->addSeparator();
}

void Facade::processToolboxAction(QAction *action)
{
    QString label=action->text();
    if(toolboxMenuItems.contains(label))
    {
        QString entry=toolboxMenuItems.value(label);
        QDesktopServices::openUrl(QUrl::fromLocalFile(entry));
    }
}

void Facade::createLocationMenu()
{
    QMenu *m_location=menu->addMenu(QStringLiteral("位置"));
    if(config.isAdvanced)
    {
        menuItems.insert("configLocation",configLocation);
        menuItems.insert("dataLocation",dataLocation);
        menuItems.insert("cacheLocation",cacheLocation);
        menuItems.insert("genericDataLocation",genericDataLocation);

        m_location->addAction("configLocation");
        m_location->addAction("dataLocation");
        m_location->addAction("cacheLocation");
        m_location->addAction("genericDataLocation");
        m_location->addSeparator();
    }

    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_LOCATION);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_location->addAction(label);
    }
    connect(m_location,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::createPropsMenu()
{
    QMenu *m_props=menu->addMenu(QStringLiteral("组件"));
    QMenu *m_note=m_props->addMenu(QStringLiteral("速记"));
    m_note->addAction(QStringLiteral("速记1 (Alt+1)"),this,&Facade::showNote);
    m_note->addAction(QStringLiteral("速记2 (Alt+2)"),this,&Facade::showNote2);
    m_props->addAction(QStringLiteral("速搜 (Alt+F2)"),this,&Facade::showSearchBox);
    m_props->addAction(QStringLiteral("计算器 (F3)"),this,&Facade::invokeCalc);
    // m_props->addAction(QStringLiteral("快捷操作（Alt+W）"),this,&Facade::showThroughPanel);
    m_props->addSeparator();

    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_PROPS);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_props->addAction(label);
    }
    connect(m_props,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::createOptionMenu()
{
    QMenu *m_option=menu->addMenu(QStringLiteral("选项"));
    QMenu *m_skin=m_option->addMenu(QStringLiteral("换肤"));
    m_skin->addAction(QStringLiteral("蓝星星"));
    m_skin->addAction(QStringLiteral("绿星星"));
    m_skin->addAction(QStringLiteral("粉星星"));
    connect(m_skin,&QMenu::triggered,this,&Facade::processsSkinAction);

    m_option->addSeparator();
    m_option->addAction(QStringLiteral("本机信息"),::sp_aboutDevice);
    m_option->addAction(QStringLiteral("应用信息"),::sp_aboutApp);
    m_option->addAction(QStringLiteral("版本信息"),this,&Facade::aboutVersion);
    m_option->addAction(QStringLiteral("硬件信息"),this,&Facade::showHardwareInfo);
    m_option->addSeparator();
    m_option->addAction(QStringLiteral("刷新菜单"),this,&Facade::reloadMenuAction);
    m_option->addAction(QStringLiteral("设置开机启动"),this,&Facade::setStartup);
    m_option->addAction(QStringLiteral("取消开机启动"),this,&Facade::unsetStartup);
    m_option->addSeparator();
    m_option->addAction(QStringLiteral("退出"),QApplication::quit);
}

void Facade::setStartup()
{
    ::sp_setStartup(true);
}

void Facade::unsetStartup()
{
    ::sp_setStartup(false);
}

void Facade::reloadMenuAction()
{
    reloadMenu();
    tray->showMessage(QStringLiteral("消息"),QStringLiteral("菜单已刷新"));
}

void Facade::showHardwareInfo()
{
    QString info=QStringLiteral("CPU：\n") + ::reg_getCPUName() + "\n"
            + QStringLiteral("主板：\n") + ::reg_getBIOSInfo();
    QMessageBox::information(0,QStringLiteral("硬件信息"), info);
}

void Facade::createAppMenu()
{
    QMenu *m_app=menu->addMenu(QStringLiteral("应用"));
    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_APP);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_app->addAction(label);
    }
    connect(m_app,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::createCommandMenu()
{
    QMenu *m_command=menu->addMenu(QStringLiteral("命令"));
    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_COMMAND);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_command->addAction(label);
    }
    connect(m_command,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::createToolMenu()
{
    QMenu *m_tool=menu->addMenu(QStringLiteral("工具"));
    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_TOOL);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_tool->addAction(label);
    }
    connect(m_tool,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::createFavoriteMenuItem()
{
    if(!bookmarks.isEmpty())
        bookmarks.clear();

    QSqlQuery query;
    QString sql=QString("select category from %1 GROUP BY category").arg(TD_BOOKMARKS);
    query.exec(sql);
    while(query.next())
    {
        QString category=query.value("category").toString();
        QMenu *m_category=m_favorite->addMenu(category);
        QSqlQuery query;
        QString sql=QString("select label,detail from %1 where category='%2'").arg(TD_BOOKMARKS).arg(category);
        query.exec(sql);
        while (query.next()) {
            QString label=query.value("label").toString();
            QString detail=query.value("detail").toString();
            bookmarks.insert(label,detail);
            m_category->addAction(label);
        }
    }
    m_favorite->addSeparator();
    m_favorite->addAction(QStringLiteral("刷新"),
                          this,&Facade::reloadFavoriteMenu);
    connect(m_favorite,&QMenu::triggered,this,&Facade::processFavoriteAction);
}

void Facade::createFavoriteMenu()
{
    m_favorite=menu->addMenu(QStringLiteral("收藏夹"));
    createFavoriteMenuItem();
}

void Facade::reloadFavoriteMenu()
{
    if(db_try())
    {
        m_favorite->clear();
        m_favorite->disconnect();
        createFavoriteMenuItem();
        tray->showMessage(QStringLiteral("消息"),QStringLiteral("收藏夹已刷新"));
    }
}

void Facade::processFavoriteAction(QAction *action)
{
    bookmarks.open(action->text());
}

void Facade::createSysToolMenu()
{
    QMenu *m_sysTool=menu->addMenu(QStringLiteral("系统工具"));
    QSqlQuery query;
    QString sql=QString(MENU_GET_VARS).arg(T_SYSTOOL);
    query.exec(sql);
    while(query.next())
    {
        QString label=query.value(0).toString();
        QString detail=query.value(1).toString();
        menuItems.insert(label,detail);
        m_sysTool->addAction(label);
    }
    connect(m_sysTool,&QMenu::triggered,this,&Facade::processMenuAction);
}

void Facade::processMenuAction(QAction *action)
{
    QString label=action->text();
    if(!menuItems.contains(label))
        return;

    QString detail=menuItems.value(label);
    if(!detail.contains(" "))
        QDesktopServices::openUrl(QUrl(detail));
    else
        QProcess::startDetached(detail);
}

void Facade::processsSkinAction(QAction *action)
{
    if(action->text() == QStringLiteral("蓝星星"))
    {
        putSkin(sk_blu);
        skin_blu;
    }
    else if(action->text() == QStringLiteral("粉星星"))
    {
        putSkin(sk_pik);
        skin_pik;
    }
    else if(action->text() == QStringLiteral("绿星星"))
    {
        putSkin(sk_gre);
        skin_gre;
    }
}

void Facade::showSearchBox()
{
    if(searchBox==nullptr)
    {
        searchBox=new SearchBox(this);
        connect(searchBox,&SearchBox::sendCmd,this,&Facade::parseCommand);

        searchBox->move(d_width /2 - 200,50);
    }
    searchBox->show();
}

void Facade::showNote()
{
    if(note==nullptr)
        note=new Note(this);

    note->setUserLand(userLand);
    note->firstLoad();
    note->move(300,100);
    note->show();
}

void Facade::showNote2()
{
    if(note2==nullptr)
        note2=new Note2(this);

    note2->setUserLand(userLand);
    note2->firstLoad();
    note2->move(400,200);
    note2->show();
}

void Facade::invokeCalc()
{
    QDesktopServices::openUrl(QUrl(Calc));
}

void Facade::ring(const QString &title, const QString &content)
{
    Bell *bell=new Bell;
    bell->setMsg(title,content);
    bell->showMe();
}

void Facade::pop(const QString &title, const QString &content)
{
    if(popup == nullptr)
        popup=new Popup(this);
    popup->showMe(title,content);
}

void Facade::say(const QString &word, unsigned sec)
{
    Torus *tor=new Torus;
    tor->setStyle(getSkin());
    curPos=this->pos();
    tor->move(curPos.x() - 450,curPos.y() - 100 );
    tor->display(word,sec);
}

void Facade::util_generateUuid()
{
    if(!config.isAdvanced)
        return;

    QString uuid=QUuid::createUuid().toString();
    qApp->clipboard()->setText(uuid);
    say(QStringLiteral("生成的UUID已经复制到剪贴板"));
}

void Facade::util_verifyMD5()
{
    if(!config.isAdvanced)
        return;

    if(isWorking)
    {
        say(QStringLiteral("稍等，上个任务还在计算中"));
        return;
    }

    form=Form_MD5;
    say(QStringLiteral("已进入MD5计算模式，请把文件拖放到我身上"));
}

void Facade::util_verifySHA1()
{
    if(!config.isAdvanced)
        return;

    if(isWorking)
    {
        say(QStringLiteral("稍等，上个任务还在计算中"));
        return;
    }

    form=Form_SHA1;
    say(QStringLiteral("已进入SHA1计算模式，请把文件拖放到我身上"));
}

void Facade::util_baiduPan()
{
    if(!config.isAdvanced)
        return;

    form=Form_Pan;
    say(QStringLiteral("已进入百度云模式，请先复制提取码然后双击我"));
}

void Facade::util_magnet()
{
    if(!config.isAdvanced)
        return;

    form=Form_Magnet;
    say(QStringLiteral("已进入磁链模式，请先复制代码然后双击我"));
}

void Facade::util_webThrough()
{
    if(!config.isAdvanced)
        return;

    form=Form_Web;
    say(QStringLiteral("已进入网址直达模式，请先复制网址然后双击我"));
}

void Facade::util_stringLength()
{
    if(!config.isAdvanced)
        return;

    form=Form_StrLen;
    say(QStringLiteral("已进入字符串长度计算模式，请先复制内容然后双击我"));
}

void Facade::util_reset()
{
    if(!config.isAdvanced)
        return;

    form=Form_None;
    say(QStringLiteral("模式已重置"));
}

void Facade::util_grubScreen()
{
    if(!config.isAdvanced)
        return;

    QPixmap pix=qApp->primaryScreen()->grabWindow(0);
    if(pix.isNull())
        return;

    // 文件名不能使用冒号！
    QString time=QDateTime::currentDateTime().toString("yyyy-MM-dd hhmmss");
    QString fileName=time + ".png";
    QString filePath=Location::picture + "/" + fileName;

    QFile file(filePath);
    file.open(QFile::WriteOnly);
    if(pix.save(&file,"PNG"))
        say(QStringLiteral("截屏已保存到图片文件夹"));
}

void Facade::receiveResult(QCryptographicHash::Algorithm method, const QByteArray &result)
{
    switch (method) {
    case QCryptographicHash::Md5:
        qApp->clipboard()->setText(QString(result));
        isWorking=false;
        QMessageBox::information(this,"MD5",QString(result) + QStringLiteral("\n已复制到剪贴板"));
        break;
    case QCryptographicHash::Sha1:
        qApp->clipboard()->setText(QString(result));
        isWorking=false;
        QMessageBox::information(this,"SHA1",QString(result) + QStringLiteral("\n已复制到剪贴板"));
        break;
    }
}
